<?php
class Table implements ArrayAccess, Iterator, Countable{
	private $table;
	private $key;
	private $fields;
	private $types;
	private $currentIndex=0;
	private static $tableArray=array();
	
	function Table($table,$key=null){
		$this->table=$table;
		//L.D set up database object
		$this->setFields();
	}
	
	/*
	 * Defines the primary key field or fields
	 */
	private function setKey($key){
		if(is_array($key)){
			$this->key=$key;
		}else{
			$this->key[]=$key;
		}
	}
	
	/*
	 * Checks if the primary key or keys for the table are set
	 */
	private function iskeySet(){
		$set=false;
		if(is_array($this->key)){
			$set=true;
			foreach($this->key as $value){
				if(!isset($this->$value))
					$set=false;
			}
		}
		
			
		return $set;
	}
	
	/*
	 * Generate where condition against primary key or keys.
	 * The key or keys are compared against the key values in held as object properties
	 */
	private function getKeyCondition(){
		$array=array();
		if(is_array($this->key)){
			foreach($this->key as $value){
				$array[]="`".$value."`='".$this->$value."'";
			}
		}
		return implode(" AND ",$array);
	}
	
	/*
	 * Generate where condition against primary key or keys.
	 * The key values are passes in as a parameter
	 */
	private function getIDConditon($id){
		if(!is_array($id)){
			$key=$this->key[0];
			$temp[$key]=$id;
			$id=$temp;
		}
		$array=array();
		if(is_array($this->key)&&count($id)==count($this->key)){
			foreach($this->key as $value){
				$array[]="`".$value."`='".$id[$value]."'";
			}
		}
		return implode(" AND ",$array);
	}
	
	/*
	 * Get the table names and store them in an array.
	 */
	private function getColumnNames(){
	$query="SHOW COLUMNS FROM `".$this->table."`";	
	    foreach(Database::runSelectQuery($query,MYSQL_ASSOC) as $value){
	    	if(isset($value["Field"])){
	    		$this->fields[]=$value["Field"];
	    	}
	    	if(isset($value["Key"])){
	    		if($value["Key"]=="PRI"){
	    			$this->key[]=$value["Field"];
	    		}
	    	}
	    	if(isset($value["Type"])){
	    		$this->types[$value["Field"]]=$value;
	     	}
	    }
	}
	
	private function isNumericType($type) {
		return (strpos($type, 'int') !== FALSE || $type == 'double' || $type == 'float' || $type == 'decimal');
	}
	
	/*
	 * When an object is created the fields are discovered for the table assigned to the object.
	 * The first time the fields are discovered they are saved in a static array.
	 * Subsequent initiations are recovered from the stored array.
	 */
	function setFields(){
		if(isset(self::$tableArray[$this->table]['fields']) && isset(self::$tableArray[$this->table]['key']) && self::$tableArray[$this->table]['types']) {
			$this->fields=self::$tableArray[$this->table]['fields'];
			$this->key=self::$tableArray[$this->table]['key'];
			$this->types=self::$tableArray[$this->table]['types'];
		}
		else{
			$this->getColumnNames();
			self::$tableArray[$this->table]['fields']=$this->fields;
			self::$tableArray[$this->table]['key']=$this->key;
			self::$tableArray[$this->table]['types']=$this->types;
		}
	}
	
	/*
	 * Initialize the object. Object properties are created corrosponding to the table fields
	 * ****redundant***** 
	 */
	private function initFields(){
		foreach($this->fields as $value){
			$this->$value=null;
		}
	}
	
	/*
	 * To be implemented when required
	 */
	public function delete($id=null,$condition=null){
		$query=null;
		$rt=0;
		if($condition!=null){
			$query="DELETE FROM `".$this->table.
					"` WHERE $condition";
		}
		else if($id!=null){
			$query="DELETE FROM `".$this->table.
					"` WHERE ".$this->getIDConditon($id);
		}else if($this->iskeySet()){
			$query="DELETE FROM `".$this->table.
					"` WHERE ".$this->getIDConditon($this->getKey());
		}
		if($query!=null){
			$result=Database::runQuery($query);
		}
		return $result=="";
	}
	
	public function getTypes(){
		return $this->types;
	}
	
	public function getTable(){
		return $this->table;
	}
	
	/*
	 * Return all the table field names
	 */
	function getFieldNames(){
		return $this->fields;
	}
	
	/*
	 * Bind an array to the table object.
	 * Each array element overrides or sets an object property.
	 */
	function bind($array){
		if(is_array($array)){
			foreach($array as $key=>$value){
				if(in_array($key,$this->fields))
					$this->$key=$value;
			}
			return true;
		}
		return false;
	}
	
	
	
	/*
	 * If the object key is set update table where the row key= the object key.
	 * Else insert a new row.
	 * 
	 * NOTE - $insert is no longor used
	 */
	function store($condition=null,$insert=false){
		$array=$this->getFields();
		$rt=false;
		if(is_array($array)){
 			if($this->iskeySet()&&$condition!=null){
 				$condition=$condition;
 				$rt=Database::update_array($this->table, $array,$condition);
 			}
 			else{
 				$rt=Database::insert_array($this->table, $array);
 				if($rt){
 					//Set key for single pri key
 					if(is_int($rt)&&count($this->key)==1){
 						$id=$this->key[0];
 						$this->$id=$rt;
 					}//Set keys for multi key table 
 					//I do not think this will be used!
 					else if(!$this->iskeySet()){
 						$this->setMultiKey($array);
 					}
 				}
 			}
		}
		return $rt;
	}
	
	/*
	 * Generate where clause to find primary key values. Then set the objects fields with the recovered values.
	 */
	function setMultiKey($array){
		$where=array();
		foreach($array as $key=>$value){
			$where[]=" `$key`='$value' ";
		}
	
		$this->load(null,implode(" AND ",$where));
	}
	

	
	/*
	 * Returns the key value if there is one primary key
	 * Returns an assoc array ($key filed name => value of that key)
	 */
	public function getKey(){
		$array=false;
		if($this->iskeySet()){
			if(count($this->key)>1){
				$array=array();
				foreach($this->key as $value){
					$array[$value]=$this[$value];
				}
			}else{
				$array=$this[$this->key[0]];
			}
		}
		return $array;
	}
	
	/*
	 * Bind the results of a query on the associated table to a collection of table  objects
	 */
	function loadTable($condition = null, $orderby = null) {
		$tableArray=new TableArray($this->table);
		$tableArray->loadTable($conditon,$orderby);
	}
	
	/*
	 * Load a table row into the object properties.
	 */
	function load($id=null,$condition=null){
		$query=null;
		$rt=0;
		if($condition!=null){
			$query="SELECT * from `".$this->table.
					"` WHERE $condition";
		}
		else if($id!=null){
			$query="SELECT * from `".$this->table.
					"` WHERE ".$this->getIDConditon($id);
		}else if($this->iskeySet()){
			$query="SELECT * from `".$this->table.
					"` WHERE ".$this->getIDConditon($this->getKey());
		}
		if($query!=null){
		
			$result=Database::runSelectQuery($query,MYSQL_ASSOC);
			if(is_array($result)){
				$rt=count($result);
				foreach($result as $value){
					$this->bind($value);
				}
			}else	
				$rt=false;
		}
		return $rt;
	}
	
	/*
	 * Clear all the associated table filed object properties
	 */
	function reset(){
		foreach($this->fields as $value){
			if(isset($this->$value))
				unset($this->$value);
		}
	}
	
	/**
	 * Checks the value being submitted for a database numeric type.
	 * If the value is not numeric and there is no default the field is not submitted.
	 *
	 * @param mixed array $fields fields and values to be inserted.
	 * @return mixed array fields and values to be inserted with numeric fields checked.
	 */
	function checkFields($fields){
		foreach($fields as $key=>$value){
			if($this->isNumericType($this->types[$key]["Type"])){
				//To be done: For non numeric-
				//Need to check for auto-incremented fields and unset
				//Otherwise default values to 0
				if(!is_numeric($value)){
					$fields[$key]=$this->types[$key]['Default'];
					if($fields[$key]=="")
						unset($fields[$key]);
				}
			}
		}
		return $fields;
	}
	
	
	/*
	 * Generate an array from the object properties that have been bound to the object.
	 */
	function getFields(){
		$fields=array();
		for ($i = 0; $i < count($this->fields); $i++) {
			if(property_exists  ( $this  , $this->fields[$i] )){
            	$field = $this->fields[$i];
              	$value = $this[$field];
				$fields[$field] = $value;
         	}
		}
		return $this->checkFields($fields);
	}
	
	
	function offsetExists($offset) {
		if(in_array($offset,$this->fields)&& isset($this->$offset))
			return true;
		return false;
	}
	
	/*
	 * Get object property via array access
	 */
	function offsetGet($offset) {
		if(in_array($offset,$this->fields)&& isset($this->$offset))
			return $this->$offset;
	    return null;
	}
	
	/*
	 * Set object property via array access
	 */
	function offsetSet($offset, $value) {
		if(in_array($offset,$this->fields))
	        $this->$offset=$value;
	       
	}
	
	function offsetUnset($offset) {
		if(isset($this->$offset))
			unset($this->$offset);
	}
	
	function count()
   	{
   		return $this->currentIndex;
   	}
   
	function current()
   	{
       $count=0;
       foreach($this->fields as $value){
       	if($this->currentIndex==$count&&isset($this->$value))
       		return $this->$value;
       	$count++;
       }
   	}

	function key()
	{	
		$count=0;
		foreach($this->fields as $value){
		if($this->currentIndex==$count&&isset($this->$value))
			return $value;
			$count++;
		}
	}

	function next()
	{
		return $this->currentIndex++;
	}

	function rewind()
	{
		$this->currentIndex = 0;
	}
	
	function valid()
       {
       $count=0;
       $index=null;
       foreach($this->fields as $value){
       	if($this->currentIndex==$count){
       		$index=$value;
       		break;
       	}
       	$count++;	
       }
		if($this->offsetExists($index)) {
     		return true;
    	} else {
        	return false;
		}
	}

	function append($value)
	{
		throw new Exception("This collection is read only");
	}

	function getIterator()
   	{
   		return $this;
   	}	
}
?>